package com.woniu.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Snowflake;
import com.alipay.api.internal.util.AlipaySignature;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniu.client.ProductClient;
import com.woniu.config.AlipayConfiguration;
import com.woniu.config.AlipayUtil;
import com.woniu.commons.exception.YogaException;
import com.woniu.commons.model.RedisKey;
import com.woniu.commons.model.Result;
import com.woniu.commons.model.StateData;
import com.woniu.commons.util.RedisUtil;
import com.woniu.dao.mapper.YGoodsItemMapper;
import com.woniu.dao.mapper.YGoodsOrderMapper;
import com.woniu.model.YGoods;
import com.woniu.model.YGoodsDto;
import com.woniu.model.dto.GoodsOrderDto;
import com.woniu.model.model.YGoodsItem;
import com.woniu.model.model.YGoodsOrder;
import com.woniu.model.param.OrderByShoppingCarParam;
import com.woniu.model.param.OrderPayParam;
import com.woniu.model.param.PageParam;
import com.woniu.model.param.PagePayParam;
import com.woniu.service.YGoodsOrderService;
import io.seata.spring.annotation.GlobalTransactional;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 胡歌
 * @since 2024年05月28日
 */
@Service
public class YGoodsOrderServiceImpl extends ServiceImpl<YGoodsOrderMapper, YGoodsOrder> implements YGoodsOrderService {


    @Resource
    private YGoodsOrderMapper goodsOrderMapper;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private ProductClient productClient;
    @Resource
    private YGoodsItemMapper goodsItemMapper;

    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private AlipayUtil alipayUtil;
    @Resource
    private AlipayConfiguration alipayConfiguration;




    /**
     * 商城订单下单接口
     *
     * @param param//收货地址 addressName;
     *                    *     //收货人 name;
     *                    *     //收货人电话  phoneNum;
     *                    *     //商品集合 goodsIdList;
     *                    *     下单人的用户id userId;
     * @return 订单的dto
     * @throws YogaException
     */
    @Override
    @GlobalTransactional(rollbackFor = Throwable.class)
    public GoodsOrderDto orderByShoppingCar(OrderByShoppingCarParam param) throws Exception {
        //1.对商品Id进行排序
        Collections.sort(param.getGoodsIdList());//对商品Id进行升序排列
        //Collections.sort() 方法用于对集合进行排序，它接受一个列表作为参数，并按照元素的自然顺序对列表进行排序
        System.out.println(param.getGoodsIdList()+"===================");
        //2查询商品的信息
        ArrayList<YGoodsItem> orderItemsList = new ArrayList<>();
        BigDecimal total = new BigDecimal(0);
        for (Integer goodsId : param.getGoodsIdList()) {
            if (redisUtil.zscore(RedisKey.userShopping(param.getUserId()), goodsId + "") == null) {
                throw new YogaException(500, "商品不存在");
            }
            //取出购物车商品信息
            Map<Object, Object> map = redisUtil.hgetall(RedisKey.shoppingInfo(param.getUserId(), goodsId));


            //redisUtil.hgetall查询一个hash数据
            YGoodsItem goodsItem = new YGoodsItem();

            goodsItem.setGoodsId(Integer.parseInt(map.get("GoodsId").toString()));
            goodsItem.setGoodsName(map.get("GoodsName").toString());
            goodsItem.setGoodsImage(map.get("GoodsImage").toString());
            goodsItem.setGoodsPrice(new BigDecimal(map.get("GoodsPrice").toString()));
            goodsItem.setGoodsNum(Integer.parseInt(map.get("GoodsNumber").toString()));


            orderItemsList.add(goodsItem);
        }
        //创建多个单锁
        RLock[] lockArray = new RLock[param.getGoodsIdList().size()];
        int index = 0;
        for (Integer bookId : param.getGoodsIdList()) {
            RLock locks = redissonClient.getFairLock(RedisKey.goodsIdLock(bookId + ""));
            lockArray[index++] = locks;
        }
        //使用数组创建多资源锁
        RLock lock = redissonClient.getMultiLock(lockArray);
        try {
            //加锁
            if (lock.tryLock(20, TimeUnit.SECONDS)) {

                //循环商品详情查询数据库
                for (YGoodsItem item : orderItemsList) {
                    Result<YGoodsDto> yGoodsDtoResult = productClient.byId(item.getGoodsId());
                    YGoodsDto dto = yGoodsDtoResult.getData();
                    if (dto == null) {
                        throw new YogaException(500, item.getGoodsName() + "该商品不存在，请删除后重新下单");
                    }
                    if (!Objects.equals(dto.getGoodsState(), StateData.GOODS_STATE_START)) {
                        throw new YogaException(500, item.getGoodsName() + "商品状态有误，请删除后重新下单");
                    }
                    if (dto.getGoodsNum() < item.getGoodsNum()) {
                        throw new YogaException(500, item.getGoodsName() + "数量不足，请修改数量");
                    }
                    BigDecimal number = new BigDecimal(item.getGoodsNum());
                    BigDecimal price = new BigDecimal(item.getGoodsPrice().toString());
                    BigDecimal money = price.multiply(number);
                    total = total.add(money);
                }
                //下单操作
                YGoodsOrder yGoodsOrder = BeanUtil.toBean(param, YGoodsOrder.class);
                //使用雪花生成订单号
                String orderNum = new Snowflake(1, 1).nextIdStr();
                //组装参数
                yGoodsOrder.setOrderNum(orderNum);//订单号
                yGoodsOrder.setCreateTime(LocalDateTime.now());//订单生成时间
                yGoodsOrder.setPrice(total);//订单价格
                yGoodsOrder.setOrderState(StateData.GOODS_ORDER_STATE_DISABLED);//刚生成订单，给订单状态一个未支付的状态
                //新增订单信息
                goodsOrderMapper.insert(yGoodsOrder);

                //循环新增订单详细信息
                for (YGoodsItem item:orderItemsList){
                    item.setOrderNum(orderNum);
                    goodsItemMapper.insert(item);
                    //冻结库存
                    Result<YGoodsDto> yGoodsDtoResult = productClient.byId(item.getGoodsId());
                    YGoodsDto goodsDto = yGoodsDtoResult.getData();
                    YGoods yGoods = BeanUtil.toBean(goodsDto, YGoods.class);
                    yGoods.setGoodsNum(yGoods.getGoodsNum() - item.getGoodsNum());
                    yGoods.setGoodsFrooze(yGoods.getGoodsFrooze() + item.getGoodsNum());
                    productClient.updateByIdGoods(yGoods);
                }
                try {
                    //删除redis中的购物车信息
                    for(Integer goodsId:param.getGoodsIdList()){
                        redisUtil.zrem(RedisKey.userShopping(param.getUserId()),goodsId+"");
                        redisUtil.delete(RedisKey.shoppingInfo(param.getUserId(),goodsId));
                    }
                }catch (YogaException e){
                    e.printStackTrace();
                    //日志记录
                }
                //将订单编号发送到延迟队列中
                GoodsOrderDto dto = BeanUtil.toBean(yGoodsOrder, GoodsOrderDto.class);
                dto.setMinutes(15);
                rabbitTemplate.convertAndSend("ordernum_delay_queue",orderNum);
                return dto;
            } else {
                throw new Exception("业务繁忙");
            }
        } finally {
            lock.unlock();//释放锁
        }

    }

    /**
     * 传一个对象，根据id去修改里面的信息
     * 在商城订单消费者里面用了，用于去修改商品的冻结量和数量
     * @param yGoodsOrder 商品对象
     * @return
     * @throws YogaException
     */
    @Override
    public void updateByIdGoodsOrder(YGoodsOrder yGoodsOrder) throws YogaException {
        goodsOrderMapper.updateById(yGoodsOrder);
    }

    /**
     * 通过一个订单编号查询一个订单回去
     * @param orderNum：订单编号
     * @return 一个订单
     */
    @Override
    public YGoodsOrder getOneId(String orderNum) throws YogaException {
        QueryWrapper<YGoodsOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("order_num",orderNum);
        YGoodsOrder yGoodsOrder = goodsOrderMapper.selectOne(wrapper);
        if (yGoodsOrder==null){
            throw new YogaException(500,"订单不存在");
        }
        return yGoodsOrder;
    }

    @Override
    public Object pay(OrderPayParam param) throws Exception {
        //1.检验传进来的参数是否正确
        QueryWrapper<YGoodsOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("order_num",param.getOrderNum());
        YGoodsOrder yGoodsOrder = goodsOrderMapper.selectOne(wrapper);
        if (yGoodsOrder==null|| !Objects.equals(yGoodsOrder.getOrderNum(), param.getOrderNum())){
            throw new YogaException(500,"订单不存在");
        }
        if (!Objects.equals(yGoodsOrder.getOrderState(), StateData.GOODS_ORDER_STATE_DISABLED)){
            throw new YogaException(500,"订单状态异常");
        }
        //2.组装参数
        PagePayParam payParam = new PagePayParam();
        payParam.setBody("yogo订单支付");
        payParam.setMoney(yGoodsOrder.getPrice().doubleValue());
        payParam.setNotifyUrl("http://xhvr7g.natappfree.cc/yGoodsOrder/notify");
        payParam.setReturnUrl("http://localhost:9091/#/UserMyOrder");
        payParam.setOrderId(yGoodsOrder.getOrderNum());
        payParam.setSubject("yoga");
        //取出数据库订单的下单时间
        long expireTime = Date.from(yGoodsOrder.getCreateTime().plusMinutes(15).atZone(ZoneId.systemDefault()).toInstant()).getTime();
        long now = new Date().getTime();
        int minute = (int)((expireTime - now)/1000/60);
        if (minute<1){
            throw new YogaException(500,"订单超时");
        }
        Date date = new Date(expireTime);
        String expire = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
        payParam.setTimeExpire(expire);//可支付的时间
        //调用支付宝工具类
        return alipayUtil.pagePay(payParam);
    }

    /**
     * 给支付宝回调的接口
     * @param
     * @return
     * @throws Exception
     */
    @Override
    public void payNotify(Map<String, String> map) throws Exception {
        //第一步验签
        boolean signVerified = AlipaySignature.rsaCheckV1(map, alipayConfiguration.getAlipayPublicKey(), "utf-8", "RSA2");
        if (signVerified) {
            //验签成功


            String status = map.get("trade_status");

            if ("TRADE_SUCCESS".equals(status)){
                String orderNum = map.get("out_trade_no");
                //支付成功
                //修改订单状态为支付
                QueryWrapper<YGoodsOrder> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("order_num",orderNum);
                YGoodsOrder yGoodsOrder = goodsOrderMapper.selectOne(queryWrapper);

                if (Objects.equals(yGoodsOrder.getOrderState(), StateData.GOODS_ORDER_STATE_DISABLED)) {
                    //如果订单状态是未支付才进下一步操作
                    yGoodsOrder.setOrderState(StateData.GOODS_ORDER_STATE_START);
                    String payTimeStr = map.get("send_pay_date");
                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                    LocalDateTime parse = LocalDateTime.parse(payTimeStr, formatter);
                    yGoodsOrder.setPayTime(parse);
                    //修改冻结量
                    List<YGoodsItem> list = goodsItemMapper.selectList(new QueryWrapper<YGoodsItem>().eq("order_num", orderNum));
                    for (YGoodsItem item :list){
                        Result<YGoodsDto> yGoodsDtoResult = productClient.byId(item.getGoodsId());
                        YGoodsDto data = yGoodsDtoResult.getData();
                        YGoods goods = BeanUtil.toBean(data, YGoods.class);
                        goods.setGoodsFrooze(goods.getGoodsFrooze() - item.getGoodsNum());
                        productClient.updateByIdGoods(goods);
                    }
                    goodsOrderMapper.updateById(yGoodsOrder);
                }
            }
        }
    }



    /**
     * 用户查询我的订单
     * @param param :pageNum; pageSize;
     * @param userId 用户id
     * @return page
     * @throws YogaException
     */
    @Override
    public Page<YGoodsOrder> selsectOrders(PageParam param, int userId) throws YogaException {
        QueryWrapper<YGoodsOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id",userId);
        Page<YGoodsOrder> page = new Page<>(param.getPageNum(), param.getPageSize());
        baseMapper.selectPage(page,wrapper);
        return page;
    }


    /**
     * 分页查询
     * @param param pageNum; pageSize;
     * @return
     * @throws YogaException
     */
    @Override
    public Page<YGoodsOrder> loadPageGoodsOrder(PageParam param) throws YogaException {
        Page<YGoodsOrder> page = new Page<>(param.getPageNum(), param.getPageSize());

        QueryWrapper<YGoodsOrder> wrapper = new QueryWrapper<>();

        baseMapper.selectPage(page,wrapper);

        return page;
    }


}
